{
  "metadata" : {
    "config" : {
      "dependencies" : {
        
      },
      "exclusions" : [
      ],
      "repositories" : [
      ],
      "sparkConfig" : {
        
      },
      "env" : {
        
      }
    },
    "language_info" : {
      "name" : "scala"
    }
  },
  "nbformat" : 4,
  "nbformat_minor" : 0,
  "cells" : [
    {
      "cell_type" : "markdown",
      "execution_count" : 0,
      "metadata" : {
        "language" : "text"
      },
      "language" : "text",
      "source" : [
        "# Accessing a Python object in Scala\n",
        "\n",
        "\n",
        "You can call methods and properties of Python objects in Scala code. Consider the following Python class:\n",
        "\n",
        "\n"
      ],
      "outputs" : [
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 1,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294552760,
          "endTs" : 1664294554003
        },
        "language" : "python"
      },
      "language" : "python",
      "source" : [
        "class MyPythonClass:\n",
        "    \"\"\"A simple example class\"\"\"\n",
        "    i = 12345\n",
        "\n",
        "    def f(self):\n",
        "        return 'hello world'\n",
        "    \n",
        "    def add(self, a, b):\n",
        "        return a + b\n",
        "    \n",
        "    def getLength(self, thing):\n",
        "        return len(thing)\n",
        "\n",
        "pyInst = MyPythonClass()"
      ],
      "outputs" : [
      ]
    },
    {
      "cell_type" : "markdown",
      "execution_count" : 2,
      "metadata" : {
        "language" : "text"
      },
      "language" : "text",
      "source" : [
        "Simple actions work in Scala:"
      ],
      "outputs" : [
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 3,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294554004,
          "endTs" : 1664294554161
        },
        "language" : "scala"
      },
      "language" : "scala",
      "source" : [
        "pyInst.f() // note that this must be called with `()` like in Python. "
      ],
      "outputs" : [
        {
          "execution_count" : 3,
          "data" : {
            "text/plain" : [
              "hello world"
            ]
          },
          "metadata" : {
            "name" : "Out",
            "type" : "PythonObject"
          },
          "output_type" : "execute_result"
        }
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 4,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294554162,
          "endTs" : 1664294554234
        },
        "language" : "scala"
      },
      "language" : "scala",
      "source" : [
        "pyInst.i"
      ],
      "outputs" : [
        {
          "execution_count" : 4,
          "data" : {
            "text/plain" : [
              "12345"
            ]
          },
          "metadata" : {
            "name" : "Out",
            "type" : "Out"
          },
          "output_type" : "execute_result"
        }
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 5,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294554240,
          "endTs" : 1664294554317
        },
        "language" : "scala"
      },
      "language" : "scala",
      "source" : [
        "pyInst.add(1, Math.PI)"
      ],
      "outputs" : [
        {
          "execution_count" : 5,
          "data" : {
            "text/plain" : [
              "4.141592653589793"
            ]
          },
          "metadata" : {
            "name" : "Out",
            "type" : "PythonObject"
          },
          "output_type" : "execute_result"
        }
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 6,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294554320,
          "endTs" : 1664294554458
        },
        "language" : "scala"
      },
      "language" : "scala",
      "source" : [
        "pyInst.getLength(Array(1, 2, 3))"
      ],
      "outputs" : [
        {
          "execution_count" : 6,
          "data" : {
            "text/plain" : [
              "3"
            ]
          },
          "metadata" : {
            "name" : "Out",
            "type" : "PythonObject"
          },
          "output_type" : "execute_result"
        }
      ]
    },
    {
      "cell_type" : "markdown",
      "execution_count" : 7,
      "metadata" : {
        "language" : "text"
      },
      "language" : "text",
      "source" : [
        "Note that passing Scala values into Python only works for primitive values right now. So, for example, the follow cell will fail because Python doesn't know what to do with a Scala `Seq`. "
      ],
      "outputs" : [
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 8,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294554460,
          "endTs" : 1664294554510
        },
        "language" : "scala"
      },
      "language" : "scala",
      "source" : [
        "pyInst.getLength(Seq(1,2,3))"
      ],
      "outputs" : [
        {
          "ename" : "zio.FiberFailure",
          "evalue" : "Fiber failed.\nA checked error was not handled.\njep.JepException: <class 'TypeError'>: object of type '$colon$colon' has no len()\n\tat Cell1.getLength(Cell1:12)\n\tat jep.python.PyCallable.call(Native Method)\n\tat jep.python.PyCallable.callAs(PyCallable.java:107)\n\tat polynote.runtime.python.PythonObject$$anonfun$1.apply(PythonObject.scala:27)\n\tat polynote.runtime.python.PythonObject$$anonfun$1.apply(PythonObject.scala:26)\n\tat zio.internal.FiberContext.evaluateNow(FiberContext.scala:490)\n\tat zio.internal.FiberContext.zio$internal$FiberContext$$run$body$2(FiberContext.scala:776)\n\tat zio.internal.FiberContext$$anonfun$29.run(FiberContext.scala:776)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat java.base/java.lang.Thread.run(Thread.java:829)\n\nFiber:Id(1664294554508,2863) was supposed to continue to:\n  a future continuation at zio.ZIO$$anonfun$run$2.apply(ZIO.scala:1730)\n  a future continuation at zio.ZIO$$anonfun$bracket_$1.apply(ZIO.scala:288)\n  a future continuation at zio.ZIO$$anonfun$run$2.apply(ZIO.scala:1730)\n  a future continuation at zio.ZIO$$anonfun$bracket_$1.apply(ZIO.scala:288)\n  a future continuation at zio.ZIO$$anonfun$run$2.apply(ZIO.scala:1730)\n  a future continuation at zio.ZIO$$anonfun$bracket_$1.apply(ZIO.scala:288)\n  a future continuation at polynote.kernel.interpreter.python.PythonInterpreter$$anon$6$$anonfun$run$1.apply(PythonInterpreter.scala:50)\n\nFiber:Id(1664294554508,2863) execution trace:\n  at polynote.runtime.python.PythonObject$$anonfun$1.apply(PythonObject.scala:26)\n  at zio.ZIO$$anonfun$effectSuspendTotal$1.apply(ZIO.scala:2791)\n  at zio.ZIO$$anonfun$bracket_$2.apply(ZIO.scala:288)\n  at zio.ZIO$$anonfun$lock$2.apply(ZIO.scala:3533)\n  at zio.blocking.package$$anonfun$effectBlocking$1.apply(package.scala:183)\n  at zio.ZIO$$anonfun$effectSuspendTotal$1.apply(ZIO.scala:2791)\n  at zio.ZIO$$anonfun$bracket_$2.apply(ZIO.scala:288)\n  at zio.internal.FiberContext$$anonfun$shift$2.apply(FiberContext.scala:666)\n  at zio.internal.FiberContext$$anonfun$shift$1.apply(FiberContext.scala:666)\n  at zio.ZIO$$anonfun$lock$2.apply(ZIO.scala:3533)\n  at zio.ZIO$$anonfun$effectSuspendTotal$1.apply(ZIO.scala:2791)\n  at zio.ZIO$$anonfun$bracket_$2.apply(ZIO.scala:288)\n  at zio.internal.FiberContext$$anonfun$1.apply(FiberContext.scala:555)\n\nFiber:Id(1664294554508,2863) was spawned by: <empty trace>",
          "traceback" : [
          ],
          "output_type" : "error"
        }
      ]
    },
    {
      "cell_type" : "markdown",
      "execution_count" : 9,
      "metadata" : {
        "language" : "text"
      },
      "language" : "text",
      "source" : [
        "Note that simple Python objects like a `List` convert to their equivalents in Java (in this case, an `ArrayList`), meaning you won't get to use many Scala specific features out of the box (such as for-comprehensions to iterate over lists). To accomodate this, we created the `PythonObject`, a wrapper over Jep's `PyObject` that provides a bit of Scala sugar. \n",
        "\n",
        "An example is shown below where the `asScalaList` method is used to enable for-comprehension over a simple `Python` list. \n",
        "\n",
        "You can find the full list of supported helper methods [here](https://github.com/polynote/polynote/blob/master/polynote-runtime/src/main/scala/polynote/runtime/python/PythonObject.scala). "
      ],
      "outputs" : [
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 10,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294636100,
          "endTs" : 1664294636243
        },
        "language" : "python"
      },
      "language" : "python",
      "source" : [
        "ids = [1234, 5678, 9012, 3456]\n",
        "typs = ['Number', 'Number', 'Number', 'Number']"
      ],
      "outputs" : [
      ]
    },
    {
      "cell_type" : "code",
      "execution_count" : 11,
      "metadata" : {
        "cell.metadata.exec_info" : {
          "startTs" : 1664294677100,
          "endTs" : 1664294677498
        },
        "language" : "scala"
      },
      "language" : "scala",
      "source" : [
        "case class ResultItem(id: Int, typ: String)\n",
        "\n",
        "val resultData = for {\n",
        "  i <- ids.asScalaList\n",
        "  t <- typs.asScalaList\n",
        "} yield ResultItem(i.as[Integer], t.as[String])\n",
        "\n",
        "resultData "
      ],
      "outputs" : [
        {
          "execution_count" : 11,
          "data" : {
            "text/plain" : [
              "List(ResultItem(1234,Number), ResultItem(1234,Number), ResultItem(1234,Number), ResultItem(1234,Number), ResultItem(5678,Number), ResultItem(5678,Number), ResultItem(5678,Number), ResultItem(5678,Number), ResultItem(9012,Number), ResultItem(9012,Number),…"
            ]
          },
          "metadata" : {
            "name" : "Out",
            "type" : "List[ResultItem]"
          },
          "output_type" : "execute_result"
        }
      ]
    }
  ]
}